using System;
using System.IO;

namespace Cirrostrata.Abstractions.Net
{
    public class HostContext : IHostContext
    {
        public Uri BaseUri { get; private set; }
        public string BasePath { get; private set; }

        public HostContext(string baseUrl, string basePath)
        {
            if (baseUrl == null)
                throw new ArgumentNullException("baseUrl");

            if (basePath == null)
                throw new ArgumentNullException("basePath");

            BaseUri = new Uri(baseUrl);
            BasePath = basePath.TrimEnd(Path.DirectorySeparatorChar);
        }

        public string RootRelativePathToAbsolutePath(string relativePath)
        {
            return String.Format("{0}{1}", BasePath, relativePath);
        }

        public string AbsolutePathToRootRelativeUrl(string absolutePath)
        {
            if (!CheckPrefix(absolutePath, BasePath))
                throw new ArgumentException(String.Format("AbsolutePath \"{0}\" is not rooted under \"{1}\"", absolutePath, BasePath));

            string relativeUrl = RootRelativePathToRootRelativeUrl(absolutePath.Substring(BasePath.Length));
            return relativeUrl;
        }

        public string RootRelativeUrlToAbsoluteUrl(string relativeUrl)
        {
            var absoluteUri = new Uri(BaseUri, relativeUrl);
            return Uri.EscapeUriString(absoluteUri.ToString());
        }

        public string AbsolutePathToAbsoluteUrl(string absolutePath)
        {
            return RootRelativeUrlToAbsoluteUrl(AbsolutePathToRootRelativeUrl(absolutePath));
        }

        public string AbsoluteUrlToRootRelativeUrl(string absoluteUrl)
        {
            absoluteUrl = Uri.UnescapeDataString(absoluteUrl);

            if (!CheckPrefix(absoluteUrl, BaseUri.AbsoluteUri))
                throw new ArgumentException(String.Format("AbsoluteUrl \"{0}\" is not rooted under \"{1}\"", absoluteUrl, BaseUri.AbsoluteUri));

            var absoluteUri = new Uri(absoluteUrl);

            return absoluteUri.AbsolutePath;
        }

        public string RelativeUrlToRootRelativeUrl(string absoluteOriginatingPath, string relativeUrl)
        {
            const string urlBackRef = "../";
            string dir = Path.GetDirectoryName(absoluteOriginatingPath);

            while (relativeUrl.StartsWith(urlBackRef))
            {
                // Move forward in the URL and backwards in the directory
                relativeUrl = relativeUrl.Substring(urlBackRef.Length);

                int lastDirSep = dir.LastIndexOf(Path.DirectorySeparatorChar);

                if (lastDirSep < BasePath.Length)
                    throw new ArgumentException("Relative URL specifies content beyond absolute originating path");

                dir = dir.Substring(0, lastDirSep);
            }

            string relativePath = RootRelativeUrlToRootRelativePath(relativeUrl);
            string absolutePath = String.Format("{0}{1}{2}", dir, Path.DirectorySeparatorChar, relativePath);
            string rootRelativeUrl = AbsolutePathToRootRelativeUrl(absolutePath);
            return rootRelativeUrl;
        }

        public string RootRelativeUrlToRootRelativePath(string relativeUrl)
        {
            string relativePath = Uri.UnescapeDataString(relativeUrl);
            relativePath = relativePath.Replace('/', '\\');
            return relativePath;
        }

        public string RootRelativePathToRootRelativeUrl(string relativePath)
        {
            string relUrl = relativePath.Replace('\\', '/');
            string escapedUrl = Uri.EscapeUriString(relUrl);
            return escapedUrl;
        }

        public string RootRelativeUrlToAbsolutePath(string relativeUrl)
        {
            return RootRelativePathToAbsolutePath(RootRelativeUrlToRootRelativePath(relativeUrl));
        }

        private static bool CheckPrefix(string text, string prefix)
        {
            if (prefix.Length > text.Length)
                return false;

            return prefix.CompareTo(text.Substring(0, prefix.Length)) == 0;
        }
    }
}